/* winc.c - implement Windows C Library
 */

#include "winc.h"

HANDLE      WcStderr    = INVALID_HANDLE_VALUE;
HANDLE      WcStdout    = INVALID_HANDLE_VALUE;
HANDLE      WcStdin     = INVALID_HANDLE_VALUE;
static int  StderrIsFile;
static int  StdoutIsFile;

void    WcInit(void)
    {
    WcStdin         = GetStdHandle(STD_INPUT_HANDLE);
    WCASSERT(WcStdin  != INVALID_HANDLE_VALUE);

    WcStdout        = GetStdHandle(STD_OUTPUT_HANDLE);
    WCASSERT(WcStdout != INVALID_HANDLE_VALUE);
    StdoutIsFile    = (GetFileType(WcStdout) != FILE_TYPE_CHAR);

    WcStderr        = GetStdHandle(STD_ERROR_HANDLE);
    WCASSERT(WcStderr != INVALID_HANDLE_VALUE);
    StderrIsFile    = (GetFileType(WcStderr) != FILE_TYPE_CHAR);
    }


static HANDLE Heap;


#if 0
/* WcWriteOut() - low-level Unicode string output function.
 *
 * If output is to console, we let WriteConsoleW() do the work.
 * Otherwise, we convert to UTF-8 before outputting.
 */
static
void    WcWriteOut(HANDLE Handle, wchar_t* UnicodeStr, size_t NChars)
    {
    int     ToConsole       = TRUE;   /* assume we are writing to console */
    DWORD   NCharsWritten;
    
    if(NChars == 0)
        NChars  = lstrlenW(UnicodeStr);
    if( (Handle == WcStdout && StdoutIsFile)
    ||  (Handle == WcStderr && StderrIsFile)
      )
        ToConsole   = FALSE;
    if(ToConsole)
        WriteConsoleW(Handle, UnicodeStr, NChars, &NCharsWritten, NULL);
    else
        {
//        WideCharToMultiByte
        }
    }

#endif

void*   WcMalloc(size_t Size)
    {
    if(Heap == NULL)
        Heap    = GetProcessHeap();
    return HeapAlloc(Heap, HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, Size);
    }
void    WcFree(void* Ptr)
    {
    DWORD   Status;

    Status  = HeapFree(Heap, 0, Ptr);
    WCASSERT(Status != 0);
    }

void*   WcRealloc(void* Ptr, size_t Size)
    {
    void*   Result;

    if(Ptr == NULL)
        Result  = WcMalloc(Size);
    else
        Result  = HeapReAlloc(Heap, HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, Ptr, Size);

    return Result;
    }

void*   WcCalloc(size_t Count, size_t Size)
    {
    if(Heap == NULL)
        Heap    = GetProcessHeap();
    return HeapAlloc(Heap, HEAP_GENERATE_EXCEPTIONS|HEAP_ZERO_MEMORY, Count * Size);
    }

char*   WcStrDupA(const char* Str);

wchar_t*WcStrDupW(const wchar_t* Str)
    {
    wchar_t*    Result;
    size_t      Len     = lstrlenW(Str) + 1;

    Result      = (wchar_t*)WcMalloc(Len * sizeof(*Str));
    lstrcpyW(Result, Str);
    return Result;
    }

wchar_t*WcStrnDupW(const wchar_t* Str, size_t Count)
    {
    wchar_t*    Result;
    wchar_t*    Output;
    size_t      Len     = Count + 1;

    Result      = (wchar_t*)WcMalloc(Len * sizeof(*Str));
    Output      = Result;
    while(Count--)
        *Output++   = *Str++;
    *Output++   = L'\0';
    return Result;
    }

/* WcFPrintfW() - our Unicode version of fprintf()
 *
 * Note that we call WriteConsole() if we think the output handle
 * points to a console window, since Write() would not produce
 */
void    WcFPrintfW(HANDLE Handle, const wchar_t* Format, ...)
    {
    va_list     Args;
    wchar_t*    Buffer;
    DWORD       CharCount, FileType;
    BOOL        Status = FALSE;

    Buffer      = (wchar_t*)WcMalloc(sizeof(wchar_t) * 1024 * 8);

    va_start(Args, Format);
    wvsprintfW(Buffer, Format, Args);
    va_end(Args);


    // ?? should use MessageBox() if no console exists???
    FileType    = GetFileType(Handle);
    if(FileType == FILE_TYPE_CHAR)
        Status = WriteConsoleW(Handle, Buffer, lstrlenW(Buffer), &CharCount, 0);
    else if(FileType == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
        {
        WCASSERT(FALSE);
        }
    else
        Status = WriteFile(Handle, Buffer, lstrlenW(Buffer)*2, &CharCount, 0);

    WCASSERT(Status != FALSE);

    WcFree(Buffer);
    }


/* WcErrorW() - emit error message and terminate process.
 *
 *
 * 2007/02/20 rlb   Add GetLastError() code.
 */
void    WcErrorW(const wchar_t* Format, ...)
    {
    va_list     Args;
    wchar_t*    Buffer;
    wchar_t*    ErrPtr;
    DWORD       CharCount;
    DWORD       LastError;

    LastError   = GetLastError();
    Buffer      = (wchar_t*)WcMalloc(sizeof(wchar_t) * 1024 * 8);

    va_start(Args, Format);
    wvsprintfW(Buffer, Format, Args);
    va_end(Args);
    ErrPtr  = Buffer + lstrlenW(Buffer);
    if(LastError != 0)
        {
        wsprintfW(ErrPtr, L"\n%ld (0x%08X)\n", LastError, LastError);
        ErrPtr  = ErrPtr + lstrlenW(ErrPtr);
        FormatMessageW(
            FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
            NULL,
            LastError,
            MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),
            ErrPtr,
            MAX_PATH,
            NULL);
        }
    // ?? should use MessageBox() if no console exists???
    WriteConsoleW(GetStdHandle(STD_ERROR_HANDLE), Buffer, lstrlenW(Buffer), &CharCount, 0);
    WcExit(1);
    }

/* WcPrintfW() - wide-char version of printing to standard output.
 *
 * For a Unicode console-mode program, "standard output" should use
 * a different function call depending on whether the standard output
 * handle has been redirected or not.
 *                                                                
 */
void    WcPrintfW(const wchar_t* Format, ...)
    {
    va_list     Args;
    wchar_t*    Buffer;
    DWORD       CharCount;
    BOOL        Status;

    WCASSERT(WcStdout != NULL);

    Buffer      = (wchar_t*)WcMalloc(sizeof(wchar_t) * 1024 * 8);

    va_start(Args, Format);
    wvsprintfW(Buffer, Format, Args);
    va_end(Args);


    // ?? should use MessageBox() if no console exists???
    if(GetFileType(WcStdout) == FILE_TYPE_CHAR)
        Status = WriteConsoleW(WcStdout, Buffer, lstrlenW(Buffer), &CharCount, 0);
    else
        Status = WriteFile(WcStdout, Buffer, lstrlenW(Buffer)*2, &CharCount, 0);

    WCASSERT(Status != 0);

    WcFree(Buffer);
    }

void    WcAssert(long Line, const char* Filename, const char* Expr)
    {
    WcErrorW(L"%hs(%4d); \"%hs\"", Filename, Line, Expr);
    }

#if 0
__int64     WcGetFileModTimeW(const wchar_t* FileName)
    {
    __int64         Result      = 0;
    HANDLE          FindHandle;
    WIN32_FIND_DATA FindData;
    union FT { FILETIME LastWrite; __int64 LastWriteInt; } Align;

    FindHandle  = FindFirstFile(FileName, &FindData);
    if(FindHandle != INVALID_HANDLE_VALUE)
        {
        /* avoid alignment issues */
        Align.LastWrite = FindData.ftLastWriteTime;
        Result          = Align.LastWriteInt;
        FindClose(FindHandle);
        }

    return Result;
    }


__int64 WcGetCurrentTime(void)
    {
    __int64     Result;
    union FT { FILETIME Current; __int64 CurrentAsInt; } Align;
    
    GetSystemTimeAsFileTime(&Align.Current);
    Result  = Align.CurrentAsInt;

    return Result;
    }
#endif

void*   WcMemCpy(void* To, const void* From, size_t NBytes)
    {
    char*   Dest    = (char*)To;
    const char* Src = (char*)From;

    while(NBytes-- > 0)
        *Dest++ = *Src++;
    return To;
    }

void*   WcMemSet(void* Target, int Char, size_t NBytes)
    {
    char*   Dest    = (char*)Target;

    while(NBytes-- > 0)
        *Dest++     = (char)Char;
    return Target;
    }

#ifdef MEMWATCH
void mwAbort(void);
#endif

void    WcExit(int Code)
    {
#ifdef MEMWATCH
    mwAbort();
#endif
    ExitProcess(Code);
    }

#if 0
/****************************************
 * Output thread functions
 */

#define WC_OUT_PAGE_SIZE

typedef struct  TOutStream
    {
    HANDLE      Handle;
    HANDLE      Event;      /* signaled whenever space available increases */
    wchar_t*    UnicodeBuf;
    size_t      CharCount;
    }           TOutStream;

static CRITICAL_SECTION OutputThreadSection;
static HANDLE           OutputThreadSemaphore;
static HANDLE           OutputThreadHandle;
static DWORD            OutputThreadId;
static TOutStream*      OutputList;

static
DWORD   OutputThread(LPVOID Ignored)
    {
    for(;;)
        {
        TOutputStream*  Rover;
        
        /* wait for someone to claim there is data to output */
        WaitForSingleObject(OutputThreadSemaphore, INFINITE);
        

        }

    return 0;
    }

/* OutputInit() - internal function to initialize our output thread environment
 */
static
HANDLE  OutputInit(void)
    {
    OutputThreadSemaphore   = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
    InitializeCriticalSection(&OutputThreadSection);
    OutputThreadHandle = CreateThread(NULL, 4096, OutputThread, NULL, &OutputThreadId);
    }

void    WcUniWrite(HANDLE Handle, wchar_t* UnicodeStr, size_t NChars)
    {
    if(OutputThread == NULL)
        OutputInit();

    }
#endif
